package com.smedi.ismedi.authoritymanagement.core.domain.service.impl;

import com.smedi.ismedi.authoritymanagement.core.domain.aggregatemodel.FrameStructure;
import com.smedi.ismedi.authoritymanagement.core.domain.aggregatemodel.TermRelationshipGroupType;
import com.smedi.ismedi.authoritymanagement.core.domain.repository.FrameStructureRepository;
import com.smedi.ismedi.authoritymanagement.core.domain.service.FrameStructureDomainService;
import com.smedi.ismedi.authoritymanagement.core.domain.service.TermDomainService;
import com.smedi.ismedi.authoritymanagement.core.infrastructure.constant.CustomError;
import com.smedi.ismedi.web.starter.exception.LogicException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Author: xulinjun
 * @Date: 2021-7-20 15:05
 */
@Slf4j
@Service
@AllArgsConstructor
public class FrameStructureDomainServiceImpl implements FrameStructureDomainService {
    private final FrameStructureRepository frameStructureRepository;

    private final TermDomainService termDomainService;

    /**
     * 校验传入的系统组织结构
     *
     * @param data
     * @param termRelationshipGroupTypes
     * @param isFirstNode                入参的顶点元素
     */
    @Override
    public <T> void validateMenuStructure(FrameStructure<T> data, List<TermRelationshipGroupType> termRelationshipGroupTypes, boolean isFirstNode) {
        String rootCode = data.getRootCode();
        String rootName = data.getRootName();
        // 是否为根节点
        boolean isRoot = data.getParentId() == null;
        // 是否有构件信息
        boolean hasTerm = !StringUtils.isEmpty(data.getType());

        // 入参根节点，判断是否有重复作为根或者子节点包含了根的属性
        if (isFirstNode) {
            // 判断当前结构中是否存在多个根节点
            if (isRoot && (StringUtils.isEmpty(rootCode) || StringUtils.isEmpty(rootName))) {
                throw LogicException.raise(CustomError.ROOT_INFO_MISS);
            }
            // 判断是否根节点编码重复
            if (frameStructureRepository.selectRootCodeCount(data.getRootCode(), data.getStructureId()) > 0) {
                throw LogicException.raise(CustomError.DUPLICATE_ROOT_CODE, null, data.getRootCode());
            }
        } else {
            // 子节点不允许包含根节点属性的任何信息
            // 从第二个节点开始不需要校验parentId是否为空，因为肯定为空
            if (!StringUtils.isEmpty(rootCode) || !StringUtils.isEmpty(rootName)) {
                throw LogicException.raise(CustomError.CHILDREN_CANNOT_HAS_ROOT_INFO, null, data.toString());
            }
        }

        // 构件类型与构件id判断
        if (StringUtils.isEmpty(data.getType()) || data.getTermId() == null) {
            // 根节点可以不绑定对象
            if (data.getParentId() != null) {
                throw LogicException.raise(CustomError.TERM_TYPE_OR_ID_MISS);
            }
        } else {
            // 判断类型是否存在
            Optional<TermRelationshipGroupType> optional = termRelationshipGroupTypes.stream().filter(rolePermissionType -> rolePermissionType.getType().equals(data.getType())).findFirst();
            if (!optional.isPresent()) {
                throw LogicException.raise(CustomError.TERM_TYPE_NOT_EXIST, null, data.toString());
            }

            // 判断当前构件是否存在
            if (termDomainService.queryTermByTermIdAndType(data.getTermId(), data.getType()) == null) {
                throw LogicException.raise(CustomError.TERM_NOT_FOUND);
            }

            // 根据类型获取允许的类型
            List<String> allows = optional.get().getChildrenRules();
            // 获取当前所有子节点的类型
            List<String> childrenTypes = data.getChildren().stream().map(FrameStructure::getType).collect(Collectors.toList());
            // 判断子节点的类型是否允许创建
            List<String> errorTypes = new ArrayList<>();
            // 判断是否存在
            Optional<String> errorType = childrenTypes.stream().filter(type -> !allows.contains(type)).findFirst();
            if (errorType.isPresent()) {
                // 如果是根节点未绑定类型，则可以不校验
                if (isRoot) {
                    if (hasTerm) errorTypes.add(errorType.get());
                } else {
                    errorTypes.add(errorType.get());
                }
            }
            if (errorTypes.size() > 0) {
                throw LogicException.raise(CustomError.INVALID_CHILDREN_TYPE, null, "当前: " + data.toString() + "，错误类型：" + errorTypes);
            }
        }

        // 子节点校验
        for (FrameStructure<T> child : data.getChildren()) {
            this.validateMenuStructure(child, termRelationshipGroupTypes, false);
        }
    }

    @Override
    public void dropChildrenByStructureId(Long structureId, boolean isFirst) {
        FrameStructure frameStructure = frameStructureRepository.selectByStructureId(structureId);
        if (frameStructure != null) {
            if (!isFirst) {
                frameStructureRepository.doDelete(structureId);
            }
            // 获取子节点
            List<FrameStructure> children = frameStructureRepository.selectByParentId(structureId);
            for (FrameStructure child : children) {
                this.dropChildrenByStructureId(child.getStructureId(), false);
            }
        } else {
            throw LogicException.raise(CustomError.FRAME_STRUCTURE_NOT_EXIST, null, structureId.toString());
        }
    }

    @Override
    public FrameStructure saveSingleFrameStructure(FrameStructure data) {
        FrameStructure basic;
        if (data.getStructureId() != null) {
            basic = frameStructureRepository.selectByStructureId(data.getStructureId());
            if (basic == null) {
                throw LogicException.raise(CustomError.FRAME_STRUCTURE_NOT_EXIST, null, String.valueOf(data.getStructureId()));
            }
        } else {
            basic = new FrameStructure();
        }

        // 设置根节点信息
        if (data.getParentId() == null) {
            basic.setRoot(true)
                    .setRootCode(data.getRootCode())
                    .setRootName(data.getRootName());
        } else {
            basic.setRoot(false)
                    .setRootCode(null)
                    .setRootName(null)
                    .setParentId(data.getParentId());
        }

        basic.setType(data.getType())
                .setTermId(data.getTermId())
                .setDisplayOrder(data.getDisplayOrder())
                .setChildren(data.getChildren());

        FrameStructure domain = (data.getStructureId() == null) ? frameStructureRepository.doInsert(basic) : frameStructureRepository.doUpdate(basic);

        // 装配构建信息
        if (domain.getTermId() != null && !StringUtils.isEmpty(domain.getType())) {
            domain.setTerm(termDomainService.queryTermByTermIdAndType(domain.getTermId(), domain.getType()));
        }
        return domain;
    }

    @Override
    public FrameStructure saveAllFrameStructure(FrameStructure data) {
        List<FrameStructure> children = data.getChildren();
        FrameStructure domain = saveSingleFrameStructure(data);
        for (FrameStructure child : children) {
            child.setParentId(domain.getStructureId());
            domain.getChildren().add(saveAllFrameStructure(child));
        }
        return domain;
    }

    @Override
    public FrameStructure queryFrameStructureByRootCode(String rootCode) {
        FrameStructure frameStructure = frameStructureRepository.selectByRootCode(rootCode);
        if (frameStructure != null) {
            return frameStructure;
        } else {
            throw LogicException.raise(CustomError.FRAME_STRUCTURE_NOT_EXIST);
        }
    }

    @Override
    public List<FrameStructure> queryChildren(Long structureId) {
        return frameStructureRepository.selectByParentId(structureId);
    }

    @Override
    public List<FrameStructure> queryAllChildren() {
        return frameStructureRepository.selectAllChildren();
    }

    @Override
    public List<FrameStructure> queryAllChildrenByStructureId(Long structureId, List<FrameStructure> children) {
        log.warn("structureId: {}", structureId);
        List<FrameStructure> list = new ArrayList<>();

        List<FrameStructure> frameStructures = this.getFrameStructure(structureId, children);
        for (FrameStructure frameStructure : frameStructures) {
            list.add(frameStructure);
            list.addAll(queryAllChildrenByStructureId(frameStructure.getStructureId(), children));
        }
        return list.stream().distinct().collect(Collectors.toList());
    }

    private List<FrameStructure> getFrameStructure(Long structureId, List<FrameStructure> children) {
        List<FrameStructure> listTemp = new ArrayList<>();

        Map<Long, List<FrameStructure>> map = children.stream().filter(x -> x.getParentId() != null).collect(Collectors.groupingBy(FrameStructure::getParentId));
        for (Map.Entry<Long, List<FrameStructure>> structureMap : map.entrySet()) {
            if (structureMap.getKey().equals(structureId)) {
                listTemp.addAll(structureMap.getValue());
            }
        }
        return listTemp.stream().distinct().collect(Collectors.toList());
    }
}
